/******************************************************************************
 * $Id: main.c,v 1.185 2004/09/23 02:09:09 jimb Exp $
 * @file
 * @author  Jim Brooks http://www.jimbrooks.org
 * @brief   "hypersrc" is a program for browsing source code using tags.
 ******************************************************************************/

#define HYPERSRC_DEFINE_GLOBALS

#include <gtksourceview/gtksourceview.h>

#include "common.h"
#include "main.h"

#include "sys.h"
#include "module.h"
#include "tags.h"
#include "funcgraph.h"
#include "widgets.h"
#include "notebook.h"
#include "statusbar.h"
#include "list.h"
#include "parser.h"
#include "language.h"

#include <setjmp.h>
sigjmp_buf main_sigjump;

//#define ENABLE_NLS

/*
static void
lv_test_hash_table(gpointer key,
		gpointer value,
		gpointer user_data)
{
	g_assert(NULL==user_data);
	g_print("key: %s	name: %s\n", (gchar*)key, ((tag_t*)value)->pType);
}
*/

/******************************************************************************
 * Main.
 ******************************************************************************/
int main( int argc, char* argv[] )
{
   gboolean doRestoreVars = TRUE;
   int		i;

#ifdef ENABLE_NLS
   /*
	* xgettext -j -k_ -kN_ -o po/hypersrc.po src/ *.c src/h/ *.h
	* msgfmt   -o po/hypersrc.mo po/hypersrc.po
	*/
   // initial for i18n
   setlocale(LC_ALL,"");
   bindtextdomain (PACKAGE_NAME, LOCALEDIR);
   bind_textdomain_codeset (PACKAGE_NAME, "UTF-8");
   textdomain (PACKAGE_NAME);
#endif

  /*
   * Some Init*() functions not called here must be called after cmd-line is processed.
   */
   InitUtils();
   InitFuncgraph();
//   InitHilite(); //lv

#if 0  &&  defined(HYPERSRC_DEBUG)
   g_print( "GTK+ version is %d.%d.%d \n",
             gtk_major_version, gtk_minor_version, gtk_micro_version );
#endif

   if ( argc == 1 )
   {
      g_print( _("Run Hypersrc.pl instead (see man hypersrc or Hypersrc.pl -help). \n") );
      exit(1);
   }

  /*
   * Init GTK+/GNOME (necessary before threading can occur).
   */
   gtk_init(&argc, &argv);
   gnome_init( PACKAGE_NAME, HYPERSRC_VERSION_STRING, 1, argv );

  /*
   * Install own signal handlers.
   */
   SysInstallSignalHandlers();

  /*
   * Specially parse -ignore-prefs
   * (other args should be parsed after vars are initialized/restored).
   */
   for ( i = 1; i < argc; ++i )
   {
      if ( MatchArg( argv[i], "-ignore-prefs" ) )
      {
         doRestoreVars = FALSE;
         break;
      }
   }

  /*
   * Initialize/restore globals (before parsing cmd line).
   */
   InitGlobals();
   if ( doRestoreVars ) RestoreVars();

  /*
   * Parse command line (after globals are initialized/restored).
   */
   ParseCommandLine( argc, argv );
   cmdLineArgsProcessed = TRUE;

  /*
   * These Init*() functions can only be called here cmd-line is processed.
   */
   InitParser();

#ifdef HYPERSRC_THREADS
#error "Threaded code was removed."
  /*
   * Even though GLIB thread functions are not used,
   * calling this seems necessary (otherwise, the tags processing
   * has been known to erratically hang, evidently, at a GLIB call).
   */
   if ( ! g_thread_supported() ) g_thread_init(NULL);
   g_assert( g_thread_supported() );
#endif /* HYPERSRC_THREADS */

  /*
   * Create enough of the widgets so that the GUI
   * has popped up and is running while tagging/graphing.
   */
#if !defined(HYPERSRC_PROFILE)  &&  !defined(HYPERSRC_SPEED)  &&  !defined(HYPERSRC_MEASURE)
   CreateWidgetsStep1();
#endif

  /*
   * Process ctags output, build module hash table.
   */
   if ( ctagsStdin )
      ProcessCtagsFromStdin();
   else
      ProcessCtagsFromFile( pTagsFilename );

   tagProcessingComplete = TRUE;

  /*
   * Build sorted list of module names (from module hash table).
   */
   CreateModuleNameGlist();

  /*
   * Build the function graph.
   */
   if ( !skipFuncTree) BuildFuncGraph();

#if defined(HYPERSRC_MEASURE)  ||  defined(HYPERSRC_PROFILE)
   gtk_exit(0);
#endif

   StatusMsg( "tags       = %d \n", tagsCnt );
   StatusMsg( "modules    = %d \n", g_hash_table_size( pHashModules ) );

  /*
   * Create the remaining widgets that depend on tag/funcgraph data.
   */
#if defined(HYPERSRC_PROFILE)  ||  defined(HYPERSRC_SPEED)
   CreateWidgetsStep1();
#endif
   CreateWidgetsStep2();

/*
   //lv ( show each items.)
   g_hash_table_foreach(pHashFuncTagsAll,
		   (GHFunc)lv_test_hash_table,
		   NULL);
*/ 
  /*
   * Enter event loop.
   */
   sigsetjmp( main_sigjump, 1 );
   gtk_main();

  /*
   * Out of the loop, exit.
   */
   gtk_exit( 0 );
   return 0;		/* shutup compiler */
}


/*****************************************************************************
 * @return True if an arg matches regardless if it has one or two dashes
 * (in order to be compatible with the GNU two-dash arg convention).
 * @param   pArg
 *          The actual argument passed from the cmd-line.
 * @param   pStr1Dash
 *          This should be a literal string (ie what to compare against) and
 *          begin with only one dash, eg: "-help".
 *****************************************************************************/
int
MatchArg( const char* pArg,
          const char* pStr1Dash )
{
   if ( !pArg  ||  !pStr1Dash ) return FALSE;

  /*
   * Does the actual arg already match?
   */
   if ( strcmp( pArg, pStr1Dash ) == 0 )
      return TRUE;

  /*
   * The actual arg didn't match.
   * If the actual arg begins with two dashes,
   * do a second comparison that ignores the first dash.
   */

   if (    strlen(pArg) > 2
        && pArg[0] == '-'
        && pArg[1] == '-' )
   {
      if ( strcmp( pArg+1, pStr1Dash ) == 0 )
         return TRUE;
   }
   return FALSE;
}

/******************************************************************************
 * Initialize globals (call before command line args are parsed).
 ******************************************************************************/
void
InitGlobals( void )
{
  /*
   * Create the hash table for loaded modules.
   */
   pHashModules = g_hash_table_new( HashModuleKey,
                                    HashModuleCmp );

  /*
   * Misc initializations (impractical to do at compile-time).
   */
   widgetTagsClistWidthCol[0]	= WIDGET_TAGS_CLIST_WIDTH_COL0;
   widgetTagsClistWidthCol[1]	= WIDGET_TAGS_CLIST_WIDTH_COL1;
   widgetTagsClistWidthCol[2]	= WIDGET_TAGS_CLIST_WIDTH_COL2;
   widgetTagsClistWidthCol[3]	= WIDGET_TAGS_CLIST_WIDTH_COL3;

   widgetHistoryClistWidthCol[0] = WIDGET_HISTORY_CLIST_WIDTH_COL0;
   widgetHistoryClistWidthCol[1] = WIDGET_HISTORY_CLIST_WIDTH_COL1;
   widgetHistoryClistWidthCol[2] = WIDGET_HISTORY_CLIST_WIDTH_COL2;
   widgetHistoryClistWidthCol[3] = WIDGET_HISTORY_CLIST_WIDTH_COL3;

   widgetModulesClistWidthCol[0] = WIDGET_MODULES_CLIST_WIDTH_COL0;
   widgetModulesClistWidthCol[1] = WIDGET_MODULES_CLIST_WIDTH_COL1;

   widgetFoundClistWidthCol[0]	= WIDGET_FOUND_CLIST_WIDTH_COL0;
   widgetFoundClistWidthCol[1]	= WIDGET_FOUND_CLIST_WIDTH_COL1;
   widgetFoundClistWidthCol[2]	= WIDGET_FOUND_CLIST_WIDTH_COL2;

   findGeom[0] = 0;
   findGeom[1] = 0;
   findGeom[2] = 0;
   findGeom[3] = 0;

   mainWindowTitle[0] = '\0';

   pGStringStatusbarMsg = g_string_new( "" );

   notebookPageWithSelectedRow = NotebookPageTags();

   cursorText.valid = FALSE;

   InitFontNames();

  /*
   * "-1" (invalid X) indicates to not change the geometry of the main window.
   */
   widgetMainGeom[0]		= -1;

   return;
}

/*****************************************************************************
 * Init font names.
 *****************************************************************************/
void
InitFontNames( void )
{
  /*
   * If a font name of a widget is empty then its font won't be changed
   * (ie let GTK choose its font).
   */
#if 0
   /* ### THIS FONT WON'T BE DISPLAYED PROPERLY ON XFREE86 4.0.1 ### */
   strcpy( fontNameText, "-misc-fixed-medium-r-*-*-*-120-*-*-*-*-*-*" );
#endif
   strcpy( fontNameText, FONT_NAME_TEXT_DEFAULT );
   strcpy( fontNameList, FONT_NAME_LIST_DEFAULT );
   strcpy( fontNameTree, FONT_NAME_TREE_DEFAULT );
/*
   strcpy( fontNameHiliteComment, FONT_NAME_C_COMMENT );
   strcpy( fontNameHiliteKeyword, FONT_NAME_C_KEYWORD );
   strcpy( fontNameHiliteType,    FONT_NAME_C_TYPE    );
   strcpy( fontNameHiliteSymbol,  FONT_NAME_C_SYMBOL  );
 */
}

/*****************************************************************************
 * Restore some variables from GNOME configuration resource.
 * Cmd-line args will be processed subsequently, thus overriding GNOME prefs.
 *****************************************************************************/
void
RestoreVars( void )
{
   int		i;
   float    f;
   int		flag;
   char		tagsWidthKey[]  = "/hypersrc/widths/tags_clist_col_?";
   char		foundWidthKey[] = "/hypersrc/widths/found_clist_col_?";
   char		historyWidthKey[] = "/hypersrc/widths/history_clist_col_?";
   char*	str;

  /*
   * Quit if no configuration was saved.
   */
   if ( gnome_config_get_bool( "/hypersrc/misc/config_flag" ) == FALSE )
      return;

  /*
   * Restore misc.  
   */
   textLeft			  =  gnome_config_get_int( "/hypersrc/misc/text_widget_at_left_side" );
   dialogPlacement	  =  gnome_config_get_int( "/hypersrc/misc/dialog_placement" );
   toolbarPlacement	  =  gnome_config_get_int( "/hypersrc/misc/toolbar_placement" );
   hilite			  =  gnome_config_get_int( "/hypersrc/misc/highlight_source_code" );
   showLineColumn	  = !gnome_config_get_int( "/hypersrc/misc/disable_line_column" );

   conserveMemory     = gnome_config_get_int_with_default( "/hypersrc/misc/conserve_memory", &flag );
   if ( flag ) conserveMemory = TRUE;
   speed = !conserveMemory;

   enableProcessPendingEvents = gnome_config_get_int_with_default( "/hypersrc/misc/process_events_while_busy", &flag );
   if ( flag ) enableProcessPendingEvents = TRUE;

   skipFuncTree = ! gnome_config_get_int_with_default( "/hypersrc/misc/build_functree", &flag );
   if ( flag ) skipFuncTree = FALSE;
   skipFuncTree_pref = skipFuncTree; /* pref code affects this */

   fontifyFuncTrees = gnome_config_get_int_with_default( "/hypersrc/misc/fontify_module_func_trees", &flag );
   if ( flag ) fontifyFuncTrees = TRUE;

   funcTreeVerbose = gnome_config_get_int_with_default( "/hypersrc/misc/functree_verbose", &flag );
   if ( flag ) funcTreeVerbose = TRUE;

   autoSelectNotebookPage = gnome_config_get_int_with_default( "/hypersrc/misc/auto_select_notebook_page", &flag );
   if ( flag ) autoSelectNotebookPage = TRUE;

   knrStyle = gnome_config_get_int_with_default( "/hypersrc/misc/knr_style", &flag );
   if ( flag ) knrStyle = FALSE;

  /*
   * Restore widget geoms.
   */
   widgetMainGeom[0] = gnome_config_get_int( "/hypersrc/geoms/main_x" );
   widgetMainGeom[1] = gnome_config_get_int( "/hypersrc/geoms/main_y" );
   widgetMainGeom[2] = gnome_config_get_int( "/hypersrc/geoms/main_w" );
   widgetMainGeom[3] = gnome_config_get_int( "/hypersrc/geoms/main_h" );

#if 0
/* Somehow this breaks KludgeForWidgetGeom() */
   findGeom[0] = gnome_config_get_int( "/hypersrc/geoms/find_x" );
   findGeom[1] = gnome_config_get_int( "/hypersrc/geoms/find_y" );
   /* skip restoring w/h */
#endif

  /*
   * Restore percents.
   */
   panedSliderPercent         = gnome_config_get_int( "/hypersrc/percents/paned_slider" );
   panedSliderFuncTreePercent = gnome_config_get_int( "/hypersrc/percents/paned_slider_functree" );
   panedSliderTextPercent     = gnome_config_get_int( "/hypersrc/percents/paned_slider_text" );

  /*
   * Restore widths.
   */
   for ( i = 0; i < WIDGET_TAGS_CLIST_COLUMNS; ++i )
   {
      tagsWidthKey[ sizeof(tagsWidthKey) - 2 ] = '0' + i;
      widgetTagsClistWidthCol[i] = gnome_config_get_int( tagsWidthKey );
   }
   for ( i = 0; i < WIDGET_HISTORY_CLIST_COLUMNS; ++i )
   {
      historyWidthKey[ sizeof(historyWidthKey) - 2 ] = '0' + i;
      widgetHistoryClistWidthCol[i] = gnome_config_get_int( historyWidthKey );
   }
   for ( i = 0; i < WIDGET_FOUND_CLIST_COLUMNS; ++i )
   {
     /*
      * If no successful search was done in all prior instances,
      * then widths for the found clist will be absent.
      */
      foundWidthKey[ sizeof(foundWidthKey) - 2 ] = '0' + i;
      if ( gnome_config_get_int( foundWidthKey ) == 0 ) break;
      widgetFoundClistWidthCol[i] = gnome_config_get_int( foundWidthKey );
   }

  /*
   * Restore numeric vars.
   */
   tabWidth = gnome_config_get_int( "/hypersrc/numbers/tab_width" );
   maxTags  = gnome_config_get_int( "/hypersrc/numbers/max_tags" );
   if ( maxTags <= 0 /*||  maxTags > MAX_TAGS_ABSOLUTE*/ )
   {
      Warning( "GNOME config: 'max_tags' is invalid or too large, using default." );
      maxTags = MAX_TAGS_DEFAULT;
   }
   f = gnome_config_get_float( "/hypersrc/numbers/mouse_wheel_normal" );
   if ( f > 0.0 ) mouseWheelNormal = f;
   f = gnome_config_get_float( "/hypersrc/numbers/mouse_wheel_fast" );
   if ( f > 0.0 ) mouseWheelFast = f;

  /*
   * Restore font names.
   */
   if ( !IsStringEmpty(str = gnome_config_get_string( "/hypersrc/fonts/text_widget"    )) ) strcpy( fontNameText, str );
   if ( !IsStringEmpty(str = gnome_config_get_string( "/hypersrc/fonts/list_widgets"   )) ) strcpy( fontNameList, str );
   if ( !IsStringEmpty(str = gnome_config_get_string( "/hypersrc/fonts/tree_widgets"   )) ) strcpy( fontNameTree, str );
/*
   if ( !IsStringEmpty(str = gnome_config_get_string( "/hypersrc/fonts/hilite_comment" )) ) strcpy( fontNameHiliteComment, str );
   if ( !IsStringEmpty(str = gnome_config_get_string( "/hypersrc/fonts/hilite_keyword" )) ) strcpy( fontNameHiliteKeyword, str );
   if ( !IsStringEmpty(str = gnome_config_get_string( "/hypersrc/fonts/hilite_type"    )) ) strcpy( fontNameHiliteType, str );
 */

//   if ( (color = gnome_config_get_int("/hypersrc/colors/text_bg_red") ) != 0 )
   {
       int color=0;
       color = gnome_config_get_int("/hypersrc/colors/text_bg_red");
       colorTextWidgetTextBg.red = color;
       color = gnome_config_get_int("/hypersrc/colors/text_bg_blue");
       colorTextWidgetTextBg.blue= color;
       color = gnome_config_get_int("/hypersrc/colors/text_bg_green");
       colorTextWidgetTextBg.green=color;
       
       color = gnome_config_get_int("/hypersrc/colors/Clist_bg_red");
       colorClistWidgetTextBg.red = color;
       color = gnome_config_get_int("/hypersrc/colors/Clist_bg_blue");
       colorClistWidgetTextBg.blue= color;
       color = gnome_config_get_int("/hypersrc/colors/Clist_bg_green");
       colorClistWidgetTextBg.green=color;   
       
       color = gnome_config_get_int("/hypersrc/colors/Sel_bg_red");
       colorClistWidgetSelBg.red = color;
       color = gnome_config_get_int("/hypersrc/colors/Sel_bg_blue");
       colorClistWidgetSelBg.blue= color;
       color = gnome_config_get_int("/hypersrc/colors/Sel_bg_green");
       colorClistWidgetSelBg.green=color;   
       
   }

  /*
   * Restore keyboard config.
   */
   str = gnome_config_get_string( "/hypersrc/keyboard/bottom_text_view" );
   if ( CompareStrings( str, gdk_keyval_name(GDK_Shift_L) ) == 0 )
   {
      auxKeyGdkKeyval = GDK_Shift_L;
      auxKeyGdkMask   = GDK_SHIFT_MASK;
   }
   else if ( CompareStrings( str, gdk_keyval_name(GDK_Control_L) ) == 0 )
   {
      auxKeyGdkKeyval = GDK_Control_L;
      auxKeyGdkMask   = GDK_CONTROL_MASK;
   }
   else /* none of the above */
   {
      if ( !IsStringEmpty(str) ) Warning( "GNOME config: 'bottom_text_view' is invalid, using default." );
   }

   str = gnome_config_get_string( "/hypersrc/keyboard/tree_update_disable" );
   if ( CompareStrings( str, gdk_keyval_name(GDK_Shift_L) ) == 0 )
   {
      treeKeyGdkKeyval = GDK_Shift_L;
      treeKeyGdkMask   = GDK_SHIFT_MASK;
   }
   else if ( CompareStrings( str, gdk_keyval_name(GDK_Control_L) ) == 0 )
   {
      treeKeyGdkKeyval = GDK_Control_L;
      treeKeyGdkMask   = GDK_CONTROL_MASK;
   }
   else if ( CompareStrings( str, gdk_keyval_name(GDK_Alt_L) ) == 0 )
   {
      treeKeyGdkKeyval = GDK_Alt_L;
      treeKeyGdkMask   = GDK_MOD1_MASK;
   }
   else /* none of the above */
   {
      if ( !IsStringEmpty(str) ) Warning( "GNOME config: 'tree_update_disable' is invalid, using default." );
   }

  /*
   * Restore searching config.
   */
   searchDefault	= gnome_config_get_int( "/hypersrc/search/search_default" );
   regexScope		= gnome_config_get_int( "/hypersrc/search/regex_scope" );
   regex = searchDefault==SEARCH_REGEX;

   /*
    * Restore editor config
    */

    bool_highlight_cur_line = gnome_config_get_bool("/hypersrc/editor/highlight_cur_line");

    bool_space_instead_of_tabs = gnome_config_get_bool("/hypersrc/editor/space_instead_of_tabs");                                    

    bool_show_line_numbers = gnome_config_get_bool("/hypersrc/editor/show_line_numbers");

    bool_smart_home_end = gnome_config_get_bool("/hypersrc/editor/smart_home_end");

    bool_auto_indent = gnome_config_get_bool("/hypersrc/editor/auto_indent");

    bool_show_margin = gnome_config_get_bool("/hypersrc/editor/show_margin");


   return;
}

/*****************************************************************************
 * Save some variables into GNOME configuration resource.
 * @pre Assumes that GNOME was init'ed.
 *****************************************************************************/
void
SaveVars( void )
{
   int		geom[4];
   int		i;
   char		tagsWidthKey[]  = "/hypersrc/widths/tags_clist_col_?";
   char		foundWidthKey[] = "/hypersrc/widths/found_clist_col_?";
   char		historyWidthKey[] = "/hypersrc/widths/history_clist_col_?";
   char*    str;

  /*
   * For next time, set a flag to indicate config data is available.
   */
   gnome_config_set_bool( "/hypersrc/misc/config_flag", TRUE );

  /*
   * Save misc data.
   */
   gnome_config_set_string( "/hypersrc/misc/version", HYPERSRC_VERSION_STRING );
   gnome_config_set_string( "/hypersrc/misc/build_date", ""__DATE__""__TIME__"" );
#ifdef HYPERSRC_DEBUG
   gnome_config_set_string( "/hypersrc/misc/build_type", "debug" );
#else
   gnome_config_set_string( "/hypersrc/misc/build_type", "release" );
#endif
   gnome_config_set_int( "/hypersrc/misc/highlight_source_code",	hilite );
   gnome_config_set_int( "/hypersrc/misc/text_widget_at_left_side",	textLeft );
   gnome_config_set_int( "/hypersrc/misc/toolbar_placement",		toolbarPlacement );
   gnome_config_set_int( "/hypersrc/misc/dialog_placement",			dialogPlacement );
   gnome_config_set_int( "/hypersrc/misc/disable_line_column",		! showLineColumn );
   gnome_config_set_int( "/hypersrc/misc/conserve_memory",			conserveMemory );
   gnome_config_set_int( "/hypersrc/misc/process_events_while_busy",enableProcessPendingEvents );
   gnome_config_set_int( "/hypersrc/misc/build_functree",           ! skipFuncTree_pref );
   gnome_config_set_int( "/hypersrc/misc/functree_verbose",         funcTreeVerbose );
   gnome_config_set_int( "/hypersrc/misc/fontify_module_func_trees",fontifyFuncTrees );
   gnome_config_set_int( "/hypersrc/misc/auto_select_notebook_page",autoSelectNotebookPage );
   gnome_config_set_int( "/hypersrc/misc/knr_style",                knrStyle );

  /*
   * Save widget geoms.
   */
   WidgetGeom( pWidgetMain, &geom[0], &geom[1], &geom[2], &geom[3] );
   gnome_config_set_int( "/hypersrc/geoms/main_x", geom[0] );
   gnome_config_set_int( "/hypersrc/geoms/main_y", geom[1] );
   gnome_config_set_int( "/hypersrc/geoms/main_w", geom[2] );
   gnome_config_set_int( "/hypersrc/geoms/main_h", geom[3] );

#if 0
/* Somehow this breaks KludgeForWidgetGeom() */
   gnome_config_set_int( "/hypersrc/geoms/find_x", findGeom[0] );
   gnome_config_set_int( "/hypersrc/geoms/find_y", findGeom[1] );
   gnome_config_set_int( "/hypersrc/geoms/find_w", findGeom[2] );
   gnome_config_set_int( "/hypersrc/geoms/find_h", findGeom[3] );
#endif

  /*
   * Save percents.
   */

   gnome_config_set_int( "/hypersrc/percents/paned_slider",          42);//PanedSliderPercent( pWidgetPaned ) );

   if ( !skipFuncTree )
      gnome_config_set_int( "/hypersrc/percents/paned_slider_functree", 78);//PanedSliderPercent( pWidgetPanedFuncTree ) );

   gnome_config_set_int( "/hypersrc/percents/paned_slider_text",     28);//PanedSliderPercent( pWidgetPanedText ) );

  /*
   * Save widths.
   */
   for ( i = 0; i < WIDGET_TAGS_CLIST_COLUMNS; ++i )
   {
      tagsWidthKey[ sizeof(tagsWidthKey) - 2 ] = '0' + i;
      gnome_config_set_int( tagsWidthKey, ClistColumnWidth( pWidgetClistTags, i ) );
   }
   for ( i = 0; i < WIDGET_HISTORY_CLIST_COLUMNS; ++i )
   {
      historyWidthKey[ sizeof(historyWidthKey) - 2 ] = '0' + i;
      gnome_config_set_int( historyWidthKey, ClistColumnWidth( pWidgetClistHistory, i ) );
   }
   if ( pWidgetClistFound )
   {
      for ( i = 0; i < WIDGET_FOUND_CLIST_COLUMNS; ++i )
      {
         foundWidthKey[ sizeof(foundWidthKey) - 2 ] = '0' + i;
         gnome_config_set_int( foundWidthKey, ClistColumnWidth( pWidgetClistFound, i ) );
      }
   }

  /*
   * Save numeric vars.
   */
   gnome_config_set_int(   "/hypersrc/numbers/tab_width",          tabWidth );
   gnome_config_set_int(   "/hypersrc/numbers/max_tags",           maxTags );
   gnome_config_set_float( "/hypersrc/numbers/mouse_wheel_normal", mouseWheelNormal );
   gnome_config_set_float( "/hypersrc/numbers/mouse_wheel_fast",   mouseWheelFast );

  /*
   * Save font names.
   */
   gnome_config_set_string( "/hypersrc/fonts/text_widget",      fontNameText );
   gnome_config_set_string( "/hypersrc/fonts/list_widgets",     fontNameList );
   gnome_config_set_string( "/hypersrc/fonts/tree_widgets",     fontNameTree );
/*
   gnome_config_set_string( "/hypersrc/fonts/hilite_comment",   fontNameHiliteComment );
   gnome_config_set_string( "/hypersrc/fonts/hilite_keyword",   fontNameHiliteKeyword );
   gnome_config_set_string( "/hypersrc/fonts/hilite_type",      fontNameHiliteType    );
 */
/*
 * Save color 
 * colorClistWidgetTextBg
 * colorTextWidgetTextBg
 * colorTextWidgetSelBg
 */
   gnome_config_set_int("/hypersrc/colors/text_bg_red", colorTextWidgetTextBg.red);
   gnome_config_set_int("/hypersrc/colors/text_bg_green", colorTextWidgetTextBg.green);
   gnome_config_set_int("/hypersrc/colors/text_bg_blue", colorTextWidgetTextBg.blue);

   gnome_config_set_int("/hypersrc/colors/Clist_bg_red", colorClistWidgetTextBg.red);
   gnome_config_set_int("/hypersrc/colors/Clist_bg_green", colorClistWidgetTextBg.green);
   gnome_config_set_int("/hypersrc/colors/Clist_bg_blue", colorClistWidgetTextBg.blue);

   gnome_config_set_int("/hypersrc/colors/Sel_bg_red", colorClistWidgetSelBg.red);
   gnome_config_set_int("/hypersrc/colors/Sel_bg_green", colorClistWidgetSelBg.green);
   gnome_config_set_int("/hypersrc/colors/Sel_bg_blue", colorClistWidgetSelBg.blue);

  /*
   * Save keyboard config.
   */
   str = gdk_keyval_name( auxKeyGdkKeyval );
   if ( str ) gnome_config_set_string( "/hypersrc/keyboard/bottom_text_view", str );

   str = gdk_keyval_name( treeKeyGdkKeyval );
   if ( str ) gnome_config_set_string( "/hypersrc/keyboard/tree_update_disable", str );

  /*
   * Save searching config.
   */
   gnome_config_set_int( "/hypersrc/search/search_default", searchDefault );
   gnome_config_set_int( "/hypersrc/search/regex_scope",    regexScope );
   /*
    * Save editor config
    */

    gnome_config_set_bool("/hypersrc/editor/highlight_cur_line",
       gtk_source_view_get_highlight_current_line(
                                      GTK_SOURCE_VIEW(pWidgetTextMain)));
    gnome_config_set_bool("/hypersrc/editor/space_instead_of_tabs",                                      
       gtk_source_view_get_insert_spaces_instead_of_tabs(
                                      GTK_SOURCE_VIEW(pWidgetTextMain)));
    gnome_config_set_bool("/hypersrc/editor/show_line_numbers",
       gtk_source_view_get_show_line_numbers(
                                      GTK_SOURCE_VIEW(pWidgetTextMain)));
    gnome_config_set_bool("/hypersrc/editor/smart_home_end",
       gtk_source_view_get_smart_home_end(
                                      GTK_SOURCE_VIEW(pWidgetTextMain)));
    gnome_config_set_bool("/hypersrc/editor/auto_indent",
       gtk_source_view_get_auto_indent(
                                      GTK_SOURCE_VIEW(pWidgetTextMain)));
    gnome_config_set_bool("/hypersrc/editor/show_margin",
       gtk_source_view_get_show_right_margin(
                                      GTK_SOURCE_VIEW(pWidgetTextMain)));

   gnome_config_sync();
   return;
}

/******************************************************************************
 * Parse the command line.
 * GNU users have a habit of using double-dashes so the common options
 * have a GNUish variant.
 ******************************************************************************/
void
ParseCommandLine( int   argc,
                  char* argv[] )
{
   int	argIdx;
   int  force = 0;

   for ( argIdx = 1; argIdx < argc; ++argIdx )
   {
      if ( MatchArg( argv[argIdx], "-force" ) )
      {
         force = TRUE;
         continue;
      }
   }

   for ( argIdx = 1; argIdx < argc; ++argIdx )
   {
     /*
      * "-help"
      */
      if (    MatchArg( argv[argIdx], "-h" )
           || MatchArg( argv[argIdx], "-help" ) )
      {
         g_print( PACKAGE_NAME "-" HYPERSRC_OLD_VER_STRING " by Jim Brooks www.jimbrooks.org/hypersrc \n");
         g_print( PACKAGE_NAME "-" HYPERSRC_VERSION_STRING " by Lvjinhua  http://www.dubuntu.com/hypersrc or http://groups.google.com/group/hypersrc\n");
         g_print( " \n" );
         g_print( "Synopsis: \n" );
         g_print( "   Source code browser and editor. \n" );
         g_print( " \n" );
         g_print( "Requires: \n" );
         g_print( "   GTK+, GNOME, Exuberant ctags, Perl, etc. \n" );
         g_print( " \n" );
         g_print( _("Usage: \n" ));
         g_print( _("   This executable 'hypersrc' isn't meant to be run directly. \n" ));
         g_print( _("   Instead, run the Perl script 'Hypersrc.pl' in a directory \n" ));
         g_print( _("   containing source code. \n" ));
         g_print( " \n" );
         g_print( "Options (mandatory but automatically passed by Hypersrc.pl): \n" );
         g_print( "   -ctags-x <filename> tags in Exuberant ctags -x format     \n" );
         g_print( "   OR                                                        \n" );
         g_print( "   -stdin              to pipe ctags -x output into hypersrc \n" );
         g_print( " \n" );
         g_print( "Options (important): \n" );
         g_print( "   -max-tags <num>     maximum amount of tags to process \n" );
         g_print( "   -max-modules <num>  maximum amount of modules (src files) to process \n" );
         g_print( "   -version            print version number and exit\n" );
         g_print( "   -ignore-prefs       skip reloading preferences \n" );
         g_print( "   -accept-all-tags    continue if a tag is unrecognized (not recommended) \n" );
         g_print( "   -conserve or -speed (mutually-exclusive) whether to conserve memory or \n" );
         g_print( "                       run faster at expense of memory consumption \n" );
         g_print( " \n" );
         g_print( "Options (misc): \n" );
         g_print( "   -force              keep going if an arg isn't recognized \n" );
         g_print( "   -status             print messages while starting \n" );
         g_print( "   -v / -q             verbose or quiet \n" );
         g_print( "   -langmap            see Exuberant ctags --langmap \n" );
         g_print( "   -knr                also parse K&R style C functions \n" );
         g_print( "   -tab-width          tab width \n" );
         g_print( "   -geom <x y w h>     geometry of main window \n" );
         g_print( "   -sort-tags=[name,module,type]  which field to sort tags by \n" );
         g_print( "   -column-widths <n1 n2 n3 n4> column widths \n" );
         g_print( "   -no-line-column     hide the tag line column \n" );
         g_print( "   -font-list          font name of the list widgets \n" );
         g_print( "   -font-text          font name of the text widget \n" );
         g_print( "   -text-left          put text widget at left side \n" );
         g_print( "   -title              window title \n" );
         g_print( "   -no-hilite          do not highlight source code \n" );
         g_print( "   -no-functree        skip building function trees (faster startup) \n" );
         g_print( "   -TagModuleTypeLine  tag column layout \n" );
         g_print( "   -TagTypeModuleLine  tag column layout \n" );
         g_print( "   -ModuleTagTypeLine  tag column layout \n" );
         g_print( "   -ModuleTypeTagLine  tag column layout \n" );
         g_print( " \n" );
         g_print( "License: \n" );
         g_print( "   GNU GPL. \n" );
         g_print( " \n" );
         g_print( "Program Updates: \n" );
         g_print( "   http://www.dubuntu.com/hypersrc \n" );
         g_print( " \n" );

         gtk_exit( 0 );
      }

     /*
      * "-ctags-x <fname>"
      *
      * This argument specifies which file holds output from ctags -x.
      */
      if ( MatchArg( argv[argIdx], "-ctags-x" ) )
      {
        /*
         * Ensure another argument follows (assumed to be the filename).
         */
         if ( argIdx + 1 >= argc ) Error( "missing argument" );

        /*
         * Set the name of the file holds output from ctags -x.
         */
         pTagsFilename = CreateString( strlen( argv[argIdx + 1] ) );
         strcpy( pTagsFilename, argv[argIdx + 1] );

        /*
         * Skip the filename argument and continue.
         */
         ++argIdx;
         continue;
      }

     /*
      * "-max-tags <num>"
      *
      * The maximum amount of tags that can be processed.
       */
      if ( MatchArg( argv[argIdx], "-max-tags" ) )
      {
        /*
         * Ensure another argument follows.
         */
         if ( argIdx + 1 >= argc ) Error( "-max-tags: missing argument" );

        /*
         * Convert following arg into a binary number.
         */
         maxTags = strtol( argv[argIdx + 1], NULL, 10 );
#if 0 /* Enforcing a hard limit is no longer done/necessary. */
         if ( maxTags < 1  ||  maxTags > MAX_TAGS_ABSOLUTE )
         {
            maxTags = MAX_TAGS_ABSOLUTE;
            Warning( "-max-tags: requested amount is excessive, limit is %d tags", maxTags );
         }
#endif
        /*
         * Skip the following argument and continue.
         */
         ++argIdx;
         continue;
      }

     /*
      * "-max-modules <num>"
      *
      * The maximum amount of modules that can be processed.
      * An absurd maximum will be ignored.
      */
      if ( MatchArg( argv[argIdx], "-max-modules" ) )
      {
        /*
         * Ensure another argument follows.
         */
         if ( argIdx + 1 >= argc ) Error( "-max-modules: missing argument" );

        /*
         * Convert following arg into a binary number.
         */
         maxModules = strtol( argv[argIdx + 1], NULL, 10 );
         if ( maxModules < 1  ||  maxModules > MAX_MODULES_ABSOLUTE )
         {
            maxModules = MAX_MODULES_DEFAULT;
            Warning( "-max-modules: overriding maximum amount" );
         }

        /*
         * Skip the following argument and continue.
         */
         ++argIdx;
         continue;
      }

     /*
      * "-tab-width <num>"
      *
      * This argument specifies the tab width.
      */
      if (    MatchArg( argv[argIdx], "-tab-width" )
           || MatchArg( argv[argIdx], "-tab" ) )
      {
        /*
         * Ensure another argument follows.
         */
         if ( argIdx + 1 >= argc ) Error( "missing argument" );

        /*
         * Convert following arg into a binary number.
         */
         tabWidth = strtol( argv[argIdx + 1], NULL, 10 );

        /*
         * If the specified tab width is unreasonable,
         * then assign zero which means to use the GTK default tab width.
         */
         if ( tabWidth < 1  ||  tabWidth > 100 )
            tabWidth = 0;

        /*
         * Skip the following argument and continue.
         */
         ++argIdx;
         continue;
      }

     /*
      * "-geom <x y w h>"
      *
      * This argument specifies the geometry of the main window.
      */
      if ( MatchArg( argv[argIdx], "-geom" ) )
      {
         int i;

        /*
         * Ensure at least 4 arguments follow.
         */
         if ( argIdx + 4 >= argc ) Error( "missing argument" );

        /*
         * Parse the geom.
         */
         for ( i = 0; i < 4; ++i )
         {
           /*
            * Try to convert this arg into a binary number.
            */
            widgetMainGeom[i] = strtol( argv[argIdx + 1], NULL, 10 );

           /*
            * Ensure this geom component is reasonable.
            */
            if (     i <= 1  /* or 0 */
                 &&  (widgetMainGeom[i] < 0  ||  widgetMainGeom[i] > MAX_GEOM_X_Y) )
            {
               Error( "-geom arg is unrealistic" );
            }
            if (     i >= 2  /* or 3 */
                 &&  (widgetMainGeom[i] < 0  ||  widgetMainGeom[i] > MAX_GEOM_W_H) )
            {
               Error( "-geom arg is unrealistic" );
            }

           /*
            * To the next arg.
            */
            ++argIdx;
         }

         continue;
      }

     /*
      * "-sort-tags=[name,module,type]"
      */
      if ( MatchArg( argv[argIdx], "-sort-tags=name" ) )
      {
         tagSortInitial = SORT_TAG;
         continue;
      }
      if ( MatchArg( argv[argIdx], "-sort-tags=module" ) )
      {
         tagSortInitial = SORT_MODULE;
         continue;
      }
      if ( MatchArg( argv[argIdx], "-sort-tags=type" ) )
      {
         tagSortInitial = SORT_TYPE;
         continue;
      }

     /*
      * "-column-widths <n1 n2 n3 n4>"
      *
      * This argument specifies the width of the tag columns.
      */
      if ( MatchArg( argv[argIdx], "-column-widths" ) )
      {
         int i;

        /*
         * Ensure at least 4 arguments follow.
         */
         if ( argIdx + 4 >= argc ) Error( "missing argument" );

        /*
         * Parse the column widths.
         */
         for ( i = 0; i < WIDGET_TAGS_CLIST_COLUMNS; ++i )
         {
           /*
            * Try to convert this arg into a binary number.
            */
            widgetTagsClistWidthCol[i] = strtol( argv[argIdx + 1], NULL, 10 );

           /*
            * Ensure the width is reasonable.
            */
            if ( widgetTagsClistWidthCol[i] <= MIN_WIDGET_TAGS_CLIST_WIDTH_COL )
            {
               widgetTagsClistWidthCol[i] = MIN_WIDGET_TAGS_CLIST_WIDTH_COL;
            }

           /*
            * To the next arg.
            */
            ++argIdx;
         }

         continue;
      }

     /*
      * "-no-line-column"
      */
      if ( MatchArg( argv[argIdx], "-no-line-column" ) )
      {
         showLineColumn = FALSE;
         continue;
      }

     /*
      * "-TagModuleTypeLine"
      */
      if ( MatchArg( argv[argIdx], "-TagModuleTypeLine" ) )
      {
         tagsColumnsLayout = TAG_COL_LAYOUT_TAG_MODULE_TYPE_LINE;
         continue;
      }

     /*
      * "-TagTypeModuleLine"
      */
      if ( MatchArg( argv[argIdx], "-TagTypeModuleLine" ) )
      {
         tagsColumnsLayout = TAG_COL_LAYOUT_TAG_TYPE_MODULE_LINE;
         continue;
      }

     /*
      * "-ModuleTagTypeLine"
      */
      if ( MatchArg( argv[argIdx], "-ModuleTagTypeLine" ) )
      {
         tagsColumnsLayout = TAG_COL_LAYOUT_MODULE_TAG_TYPE_LINE;
         continue;
      }

     /*
      * "-ModuleTypeTagLine"
      */
      if ( MatchArg( argv[argIdx], "-ModuleTypeTagLine" ) )
      {
         tagsColumnsLayout = TAG_COL_LAYOUT_MODULE_TYPE_TAG_LINE;
         continue;
      }

     /*
      * "-font-list <font>"
      * "-font-text <font>"
      *
      * These arguments specify the fonts used for clist and text widgets.
      */
      if (    MatchArg( argv[argIdx], "-font-list" )
           || MatchArg( argv[argIdx], "-font-text" ) )
      {
        /*
         * Ensure another argument follows.
         */
         if ( argIdx + 1 >= argc ) Error( "missing argument" );

        /*
         * Record the font name in the appropriate var.
         */
         if ( MatchArg( argv[argIdx], "-font-list"  ) )
            strcpy( fontNameList, argv[argIdx + 1] );
         else
            strcpy( fontNameText, argv[argIdx + 1] );

        /*
         * Skip the following argument and continue.
         */
         ++argIdx;
         continue;
      }

     /*
      * "-color-x-fg <r g b>"
      * "-color-x-bg <r g b>"
      *
      * These arguments specify the colors used for clist and text widgets.
      */
      if (    MatchArg( argv[argIdx], "-color-fg-list" )
           || MatchArg( argv[argIdx], "-color-bg-list" )
           || MatchArg( argv[argIdx], "-color-fg-text" )
           || MatchArg( argv[argIdx], "-color-bg-text" ) )
      {
         int	  i;
         long     colorPart;
         GdkColor colorRgb;

        /*
         * Ensure at least 3 arguments follow.
         */
         if ( argIdx + 3 >= argc ) Error( "missing argument" );

        /*
         * Parse the colors.
         */
         for ( i = 0; i < 3; ++i )
         {
           /*
            * Try to convert this arg into a binary number.
            */
            colorPart = strtol( argv[argIdx + 1], NULL, 16 );

           /*
            * Ensure the color is reasonable.
            * TODO
            */

           /*
            * Store the parsed color in the appropriate RGB elem.
            */
            switch ( i )
            {
               case 0:
                  colorRgb.red   = colorPart; break;
               case 1:
                  colorRgb.green = colorPart; break;
               case 2:
                  colorRgb.blue  = colorPart; break;
            }

           /*
            * To the next arg.
            */
            ++argIdx;

           /*
            * When all three color parts have been parsed,
            * assign the GdkColor array of the specified widget.
            */
            if ( i == 2 )
            {
               if ( MatchArg( argv[argIdx - 3], "-color-fg-list" ) )
                  colorClistWidgetTextFg = colorRgb;
               else
               if ( MatchArg( argv[argIdx - 3], "-color-bg-list" ) )
                  colorClistWidgetTextBg = colorRgb;
               else
               if ( MatchArg( argv[argIdx - 3], "-color-fg-text" ) )
                  colorTextWidgetTextFg = colorRgb;
               else
               if ( MatchArg( argv[argIdx - 3], "-color-bg-text" ) )
                  colorTextWidgetTextBg = colorRgb;
            }
         }
         continue;
      }

     /*
      * "-text-left"
      *
      * If present then put the text widget at the left side.
      */
      if ( MatchArg( argv[argIdx], "-text-left" ) )
      {
         textLeft = TRUE;
         continue;
      }

     /*
      * "-title"
      */
      if ( MatchArg( argv[argIdx], "-title" ) )
      {
        /*
         * Ensure another argument follows.
         */
         if ( argIdx + 1 >= argc ) Error( "missing argument" );

        /*
         * Record the passed window title.
         */
         strcpy( mainWindowTitle, argv[argIdx + 1] );

        /*
         * Skip the following argument and continue.
         */
         ++argIdx;
         continue;
      }

     /*
      * "-version"
      */
      if ( MatchArg( argv[argIdx], "-version"  ) )
      {
         g_print( "%s\n", HYPERSRC_VERSION_STRING );
         gtk_exit( 0 );
      }

     /*
      * "-gtk-version"
      */
      if ( MatchArg( argv[argIdx], "-gtk-version"  ) )
      {
         fprintf( stderr, "GTK+ %d.%d.%d is on this system. \n",
                  gtk_major_version, gtk_minor_version, gtk_micro_version );
         gtk_exit( 0 );
      }

     /*
      * "-no-hilite"
      */
      if ( MatchArg( argv[argIdx], "-no-hilite"  ) )
      {
         hilite = FALSE;
         continue;
      }

     /*
      * "-no-functree"
      */
      if ( MatchArg( argv[argIdx], "-no-functree"  ) )
      {
         skipFuncTree = skipFuncTree_pref = TRUE;
         continue;
      }

     /*
      * "-accept-all-tags"
      */
      if ( MatchArg( argv[argIdx], "-accept-all-tags"  ) )
      {
         acceptAllTags = TRUE;
         continue;
      }

     /*
      * "-conserve or -speed"
      */
      if ( MatchArg( argv[argIdx], "-conserve"  ) )
      {
         conserveMemory = TRUE;
         speed          = FALSE;
         continue;
      }
      if (    MatchArg( argv[argIdx], "-speed" )
           || MatchArg( argv[argIdx], "-fast"  ) )
      {
         speed          = TRUE;
         conserveMemory = FALSE;

         argSpeed       = TRUE; /* stays true */
         continue;
      }

     /*
      * "-stdin"
      */
      if ( MatchArg( argv[argIdx], "-stdin"  ) )
      {
         ctagsStdin = TRUE;
         continue;
      }

     /*
      * "-status" (currently same as "-v")
      */
      if (    MatchArg( argv[argIdx], "-status"  )
           || MatchArg( argv[argIdx], "-v"       ) 
           || MatchArg( argv[argIdx], "-verbose" ) ) /* MatchArg() will match two dashes */
      {
         argVerbose = TRUE;
         printStatusMsgs = TRUE;
         continue;
      }

     /*
      * "-q"
      */
      if (    MatchArg( argv[argIdx], "-q" )
           || MatchArg( argv[argIdx], "-quiet" ) ) /* MatchArg() will match two dashes */
      {
         argQuiet = TRUE;
         printStatusMsgs = FALSE;
         continue;
      }

     /*
      * "-ignore-prefs"
      */
      if ( MatchArg( argv[argIdx], "-ignore-prefs"  ) )
      {
        /*
         * Was already handled very early.
         */
         continue;
      }

     /*
      * Exuberant's "--langmap"
      */
      if ( strstr( argv[argIdx], "-langmap" ) ) /* MatchArg() won't work */
      {
        /*
         * Passing multiple -langmap args is supported.
         */
         if ( langmapCnt < LANGMAP_MAX )
         {
            langmap[langmapCnt] = g_strdup( argv[argIdx] );
            ++langmapCnt;
         }
         continue;
      }

     /*
      * "-knr" or "-oldc" (-knr is official)
      * Implying "old C" is inaccurate as the Linux kernel has a few K&R functions.
      */
      if (    MatchArg( argv[argIdx], "-knr"   )
           || MatchArg( argv[argIdx], "-oldc"  ) )
      {
         knrStyle = TRUE;
         continue;
      }

     /*
      * "-vim-exists" (INTERNAL, passed by Hypersrc.pl)
      */
      if ( MatchArg( argv[argIdx], "-vim-exists"  ) )
      {
         vimExists = TRUE;
         continue;
      }

     /*
      * "-devhelp-exists"
      */
      if ( MatchArg( argv[argIdx], "-devhelp-exists" ) )
      {
          devhelpExists = TRUE;
          continue;
      }
     /*
      * "-cplusplus" (INTERNAL, passed by Hypersrc.pl)
      */
      if ( MatchArg( argv[argIdx], "-cplusplus"  ) )
      {
         browsingCPlusPlus = TRUE;
         continue;
      }

     /*
      * "-java" (INTERNAL, passed by Hypersrc.pl)
      */
      if ( MatchArg( argv[argIdx], "-java"  ) )
      {
         browsingJava = TRUE;
         continue;
      }

     /*
      * "-class-qual" (INTERNAL, passed by Hypersrc.pl)
      */
      if ( MatchArg( argv[argIdx], "-class-qual"  ) )
      {
         classQual = TRUE;
         continue;
      }

     /*
      * Easter egg.
      */
      if (    MatchArg( argv[argIdx], "-easter"   )
           || MatchArg( argv[argIdx], "-egg"      )
           || MatchArg( argv[argIdx], "-underdog" ) )
      {
         Underdog();
         continue;
      }

     /*
      * Oops, arg was not recognized.
      */
      ErrorMsg( "\nhypersrc didn't recognize this arg: '%s'\nSee 'hypersrc -help' and 'Hypersrc.pl -help'.", argv[argIdx] );
      if ( ! force ) gtk_exit( 1 );
   }

   return;
}

/*****************************************************************************
 * Easter egg.
 *****************************************************************************/
void
Underdog( void )
{
   printf(
           "------------------------------------------------------------------------------\n"
           " Look! Up in the sky! It's a\n"
           "  bird! It's a plane! It's a\n"
           "  frog! ...A frog???                ,.+----.\n"
           "                               ,*''         I\n"
           "              ____           /         Mbp. dP          __\n"
           "          ,o@QBBBBBbm,_     ; ,d       /~`QMP'\\     _odBBBBbo,\n"
           "          OBBROYALBBBBBBBBmOBBBM      |    |   |,*~dBBBBBBBBBBBb \n"
           "          OBBBBBBBBBBqBBBBBBBBBP      |  0 |0 ,'  dBBBBBBBBBBBBBb\n"
           "          '*BBBP'     ^OBBBP'          \\__/  ~    OBBBBBBBBBBBBBP\n"
           "      /                     :           __.       `0BBBBBBBBBBP|\n"
           "                    .-.     `          ~,A.         `0BBBBBBBBP |\n"
           "                 .-(   `x__  `\\        .B*Bb          `'?OOP~   ;\n"
           "              .-(   `\\/'   `.  `~--++*\\{  `Pb           ,'     ,'\n"
           "   /         (   `\\_/'\\__/  )     _    !`-' !`.       ,'     ,'  \n"
           "        /     `\\_/'       ,'     L '+,_|    |-,`.__ ,'    _,','~~\\.~~~\\\n"
           "                 \\        /\\    / ',   '+.  |/ >   `~~~~~~  ,'    :/~  \\\n"
           "    /             `~===\\/'  `\\/'    '+.   >{  /\\        __.,: |   ;    ;\n"
           "           /           {         ,|    `\\/  \\/  `Y~~---~   `. `--:'~~~';\n"
           "                        `\\     ,&#|    uuu    uuu \\         `,   ;^`~~';\n"
           "        /               ,d#b,.&###|    UUU.   UUU. `\\     __.;~~' `~~~'\n"
           "      /              ,d###########|    UUU;   |UU:   Y~~~~\n"
           "                 ,d############i~      `UUUUUUUUU;   I  \n"
           "           ,d##################I        `UUUUUUU'    I     /   /\n"
           " (#############################b.                    Im \n"
           "    )############################b.      \\           d#b\n"
           "   (###############################b++g+++~#b      ,d##P\n"
           "       }####################P~ ~Y####P    J#P`~~~T'\n"
           "      (###################P'     `YP'    ;P'   ,'\n"
           "        )###############P'        '    ,'    ,'    /\n"
           "       (##############P'          __+~'   /##'\n"
           "  /            Y####P'         ;-'     /###P\n"
           "            /   Y#P'         /'      /&##P~\n"
           "                 |         /'      /&#P~         Not bird, nor plane, nor\n"
           "      /          `\\______/'\\_____/'              even frog. Just little ol'\n"
           "                                    /            me Underdog!\n"
           "                           /\n"
           "              /                          \n"
           "------------------------------------------------------------------------------\n"
    );
   return;
}
